package com.sc.nft.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sc.nft.dao.DigitalCollectionDao;
import com.sc.nft.dao.EcologyPoolDao;
import com.sc.nft.dao.EcologyPoolRedPackCheckDao;
import com.sc.nft.dao.EcologyPoolRedPackDao;
import com.sc.nft.dto.EcologyPoolIncreasePointsDTO;
import com.sc.nft.dto.EcologyPoolReducePointsDTO;
import com.sc.nft.dto.OpenRedPackMessageDTO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.WebEcologyPoolRedPackPageDO;
import com.sc.nft.entity.vo.EcologyPoolRedPackReceiveVO;
import com.sc.nft.entity.vo.ecologypool.*;
import com.sc.nft.enums.*;
import com.sc.nft.enums.ecologypool.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.service.*;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.util.LocalDateTimeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 品牌红包服务接口实现
 *
 * @author jinpinhai
 * @since 2024-08-10 11:40:15
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class EcologyPoolRedPackServiceImpl implements EcologyPoolRedPackService {

    private final EcologyPoolRedPackDao ecologyPoolRedpackDao;
    private final EcologyPoolRedPackReceiveService ecologyPoolRedPackReceiveService;
    private final EcologyPoolService ecologyPoolService;
    private final EcologyPoolPrizeService ecologyPoolPrizeService;
    private final UserCreatorInfoService userCreatorInfoService;
    private final EcologyPoolRelationConditionBindService ecologyPoolRelationConditionBindService;
    private final DigitalCollectionService digitalCollectionService;
    private final StringRedisTemplate stringRedisTemplate;
    private final UserCollectionService userCollectionService;
    private final DigitalCollectionDao digitalCollectionDao;
    private final MessageQueueHelper messageQueueHelper;
    private final EcologyPoolRedPackCheckDao ecologyPoolRedPackCheckDao;
    private final EcologyPoolDao ecologyPoolDao;
    private DefaultRedisScript<String> redisScript;

    @PostConstruct
    public void init() {
        ClassPathResource classPathResource = new ClassPathResource("lua/redPack.lua");
        ResourceScriptSource resourceScriptSource = new ResourceScriptSource(classPathResource);
        redisScript = new DefaultRedisScript<>();
        redisScript.setResultType(String.class);
        redisScript.setScriptSource(resourceScriptSource);
    }

    @Override
    public List<EcologyPoolDaoCommunityServiceRankIncomePrizeVo> daoCommunityServiceRankIncomePrizes() {
        List<EcologyPoolDaoCommunityServiceRankIncomePrizeVo> ecologyPoolDaoCommunityServiceRankIncomePrizeVos = Lists.newArrayList();
        for (EcologyPoolLinkTypeEnum ecologyPoolLinkTypeEnum : EcologyPoolLinkTypeEnum.values()) {
            EcologyPoolDaoCommunityServiceRankIncomePrizeVo communityServiceRankIncomePrizeVo = new EcologyPoolDaoCommunityServiceRankIncomePrizeVo();
            communityServiceRankIncomePrizeVo.setPrizeType(ecologyPoolLinkTypeEnum);
            communityServiceRankIncomePrizeVo.setPrizeTypeName(ecologyPoolLinkTypeEnum.getDesc());
            ecologyPoolDaoCommunityServiceRankIncomePrizeVos.add(communityServiceRankIncomePrizeVo);
        }
        return ecologyPoolDaoCommunityServiceRankIncomePrizeVos;
    }

    @Override
    public IPage<EcologyPoolRedPackPageVO> daoRedPackPage(Long daoId, Integer pageNo, Integer pageNum, String name, EcologyPoolRedPackAuditStatusEnum auditStatus) {
        Page<EcologyPoolRedPack> page = ecologyPoolRedpackDao.page(new Page<>(pageNo, pageNum), daoId, name, auditStatus);
        List<Long> ids = page.getRecords().stream().map(EcologyPoolRedPack::getId).collect(Collectors.toList());

        Map<Long, EcologyPoolPrize> ecologyPoolPrizeMap = ecologyPoolPrizeService.listPrizeByIdsToMap(ids, EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);
        List<Long> daoIds = page.getRecords().stream().map(EcologyPoolRedPack::getDaoId).collect(Collectors.toList());
        Map<Long, UserCreatorInfo> daoByIdsToMap = userCreatorInfoService.getDaoByIdsToMap(daoIds);

        return page.convert(x -> getDaoRedPackPageVO(x, daoByIdsToMap, ecologyPoolPrizeMap));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRedPackPage(Long daoId, String name, EcologyPoolRedPackMechanismEnum mechanism, EcologyPoolRedPackTypeEnum type, BigDecimal rewardNumber, Long packNumber, Date startTime, Date endTime, String activeRule, Boolean participateType, List<Long> participateNft, Integer participateNumber) {
        //校验积分
        checkBrandPoint(daoId, type, rewardNumber);

        //保存红包
        EcologyPoolRedPack ecologyPoolRedPack = saveEcologyPoolRedPack(daoId, name, mechanism, type, startTime, endTime, activeRule, participateType, participateNumber);

        //保存红包绑定的条件藏品
        saveNftBind(participateType, participateNft, ecologyPoolRedPack);

        //保存红包份额和总额
        saveRedPackNumber(rewardNumber, packNumber, ecologyPoolRedPack);

        List<Long> ids = Lists.newArrayList(daoId);
        Map<Long, UserCreatorInfo> creatorInfoMap = userCreatorInfoService.getDaoByIdsToMap(ids);
        //扣减生态池红包
        EcologyPoolReducePointsDTO build = EcologyPoolReducePointsDTO.builder()
                .linkCount(rewardNumber)
                .linkType(EcologyPoolLinkTypeEnum.POINTS)
                .daoId(daoId)
                .poolType(EcologyPoolTypeEnum.BRAND)
                .operationType(EcologyPoolOperationTypeEnum.CREATE_RED_PACK)

                .sourcePoolType(EcologyPoolTypeEnum.RED_PACK)
                .sourceDaoId(daoId)
                .sourceRelationId(ecologyPoolRedPack.getId())

                .transactionUuid(IdUtil.simpleUUID())
                .activeOperatorName(EcologyPoolTypeEnum.RED_PACK.getDesc())
                .passiveOperatorName(getDaoName(daoId, creatorInfoMap))
                .build();
        ecologyPoolService.reducePoints(build);
    }

    @Override
    public EcologyPoolRedPackDetailVO redPackPageDetail(Long id) {
        EcologyPoolRedPack ecologyPoolRedPack = ecologyPoolRedpackDao.getById(id);
        if (Objects.isNull(ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("红包活动不存在");
        }
        Map<Long, EcologyPoolPrize> longEcologyPoolPrizeMap = ecologyPoolPrizeService.listPrizeByIdsToMap(Lists.newArrayList(ecologyPoolRedPack.getId()), EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);

        EcologyPoolRedPackDetailVO ecologyPoolRedPackDetailVO = new EcologyPoolRedPackDetailVO();
        ecologyPoolRedPackDetailVO.setId(id);
        ecologyPoolRedPackDetailVO.setName(ecologyPoolRedPack.getName());
        ecologyPoolRedPackDetailVO.setMechanism(ecologyPoolRedPack.getMechanism());
        ecologyPoolRedPackDetailVO.setMechanismName(ecologyPoolRedPack.getMechanism().getDesc());
        ecologyPoolRedPackDetailVO.setType(ecologyPoolRedPack.getType());
        ecologyPoolRedPackDetailVO.setTypeName(ecologyPoolRedPack.getType().getDesc());
        EcologyPoolPrize ecologyPoolPrize = longEcologyPoolPrizeMap.get(ecologyPoolRedPack.getId());
        if (Objects.nonNull(ecologyPoolPrize)) {
            ecologyPoolRedPackDetailVO.setRewardNumber(ecologyPoolPrize.getLinkCount());
            ecologyPoolRedPackDetailVO.setPackNumber(ecologyPoolPrize.getLinkNumber());
        }
        ecologyPoolRedPackDetailVO.setStartTime(ecologyPoolRedPack.getStartTime());
        ecologyPoolRedPackDetailVO.setEndTime(ecologyPoolRedPack.getEndTime());
        ecologyPoolRedPackDetailVO.setActiveRule(ecologyPoolRedPack.getActiveRule());
        ecologyPoolRedPackDetailVO.setParticipate(ecologyPoolRedPack.getParticipate());
        ecologyPoolRedPackDetailVO.setParticipateNumber(ecologyPoolRedPack.getParticipateNumber());
        List<EcologyPoolRelationConditionBind> list = ecologyPoolRelationConditionBindService.listByRedPackBind(ecologyPoolRedPack.getId());
        if (CollectionUtils.isNotEmpty(list)) {
            List<String> nftNames = Lists.newArrayList();
            List<Long> nftIds = list.stream().map(EcologyPoolRelationConditionBind::getRelationId).collect(Collectors.toList());
            List<DigitalCollection> nftList = digitalCollectionService.getCollectionListById(nftIds);
            List<EcologyPoolRedPackDetailNftVO> nftVOList = Lists.newArrayList();
            for (DigitalCollection digitalCollection : nftList) {
                nftNames.add(digitalCollection.getFirstTitle());
                EcologyPoolRedPackDetailNftVO ecologyPoolRedPackDetailNftVO = new EcologyPoolRedPackDetailNftVO();
                ecologyPoolRedPackDetailNftVO.setNftName(digitalCollection.getFirstTitle());
                ecologyPoolRedPackDetailNftVO.setId(digitalCollection.getId());
                nftVOList.add(ecologyPoolRedPackDetailNftVO);
            }
            if (CollectionUtils.isNotEmpty(nftNames)) {
                ecologyPoolRedPackDetailVO.setNftName(StringUtils.join(nftNames, ","));
            }
            ecologyPoolRedPackDetailVO.setParticipateNft(nftVOList);
        }
        return ecologyPoolRedPackDetailVO;
    }

    @Override
    public IPage<EcologyPoolRedPackReceiveVO> redPackDataPage(Integer pageNo, Integer pageSize, Long id, String userTel) {
        return ecologyPoolRedPackReceiveService.daoRedPackReceivePage(pageNo, pageSize, id, userTel);
    }

    @Override
    public EcologyPoolRedPackDataDetailVO redPackDataDetail(Long id) {
        EcologyPoolPrize ecologyPoolPrize = ecologyPoolPrizeService.getEcologyPoolPrizeByBindId(id, EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);
        if (Objects.isNull(ecologyPoolPrize)) {
            throw new GlobalRunTimeException("积分配置不存在");
        }
        EcologyPoolRedPackDataDetailVO ecologyPoolRedPackDataDetailVO = new EcologyPoolRedPackDataDetailVO();
        ecologyPoolRedPackDataDetailVO.setSourceTotal(ecologyPoolPrize.getLinkCount());
        ecologyPoolRedPackDataDetailVO.setRedPackTotal(ecologyPoolPrize.getLinkNumber());
        ecologyPoolRedPackDataDetailVO.setSourceResidue(ecologyPoolPrize.getRemainCount());
        ecologyPoolRedPackDataDetailVO.setRedPackResidue(ecologyPoolPrize.getRemainNumber());
        ecologyPoolRedPackDataDetailVO.setRedPackReceive(ecologyPoolPrize.getLinkNumber() - ecologyPoolPrize.getRemainNumber());
        return ecologyPoolRedPackDataDetailVO;
    }

    @Override
    public void redPackOpen(Long id, Boolean open) {
        EcologyPoolRedPack ecologyPoolRedPack = ecologyPoolRedpackDao.getById(id);
        if (Objects.isNull(ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("红包活动不存在");
        }
        if (open && ecologyPoolRedPack.getAuditStatus() != EcologyPoolRedPackAuditStatusEnum.APPROVED) {
            throw new GlobalRunTimeException("审核中,审核拒绝无法开启");
        }

        EcologyPoolRedPack poolRedPack = new EcologyPoolRedPack();
        poolRedPack.setStatus(open);
        poolRedPack.setVersion(ecologyPoolRedPack.getVersion());

        if (!ecologyPoolRedpackDao.updateRedPackById(id, poolRedPack)) {
            throw new GlobalRunTimeException("红包活动开启关闭失败!");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeRedPack(Long id) {
        EcologyPoolRedPack redPack = ecologyPoolRedpackDao.getById(id);
        if (Objects.isNull(redPack)) {
            throw new GlobalRunTimeException("红包活动不存在");
        }
        LocalDateTime startTime = LocalDateTimeUtil.dateToLocalDateTime(redPack.getStartTime());
        if (LocalDateTime.now().isAfter(startTime)) {
            throw new GlobalRunTimeException("活动开始中不允许删除");
        }
        Map<Long, UserCreatorInfo> creatorInfoMap = userCreatorInfoService.getDaoByIdsToMap(Lists.newArrayList(redPack.getDaoId()));
        doReducePool(redPack, creatorInfoMap);
        if (!ecologyPoolRedpackDao.removeById(id)) {
            throw new GlobalRunTimeException("删除失败!");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditRedPack(Long id, EcologyPoolRedPackAuditStatusEnum ecologyPoolRedPackAuditStatusEnum, String rejectReason) {
        EcologyPoolRedPack ecologyPoolRedPack = ecologyPoolRedpackDao.getById(id);

        checkStatus(ecologyPoolRedPack);

        EcologyPoolPrize ecologyPoolPrize = ecologyPoolPrizeService.getEcologyPoolPrizeByBindId(ecologyPoolRedPack.getId(), EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);
        //通过
        doApproved(ecologyPoolRedPackAuditStatusEnum, ecologyPoolRedPack, ecologyPoolPrize);
        //拒绝
        doReject(ecologyPoolRedPackAuditStatusEnum, ecologyPoolRedPack, ecologyPoolPrize);
        //更新红包审核状态
        updateRedPackAuditStatus(id, ecologyPoolRedPackAuditStatusEnum, rejectReason, ecologyPoolRedPack);
    }


    @Override
    public List<EcologyPoolRedPackCollectionVO> listDaoDigitalCollection(Long daoId) {
        List<EcologyPoolRedPackCollectionVO> ecologyPoolRedPackCollectionVOS = Lists.newArrayList();
        List<DigitalCollection> digitalCollections = digitalCollectionDao.listByUserCreatorId(daoId);
        for (DigitalCollection digitalCollection : digitalCollections) {
            EcologyPoolRedPackCollectionVO ecologyPoolRedPackCollectionVO = new EcologyPoolRedPackCollectionVO();
            ecologyPoolRedPackCollectionVO.setCollectionId(digitalCollection.getId());
            ecologyPoolRedPackCollectionVO.setCollectionName(digitalCollection.getFirstTitle());
            ecologyPoolRedPackCollectionVOS.add(ecologyPoolRedPackCollectionVO);
        }
        return ecologyPoolRedPackCollectionVOS;
    }


    @Override
    public WebEcologyPoolRedPackDetailVO redPackDetail(Long userId, Long id) {
        EcologyPoolRedPack ecologyPoolRedPack = ecologyPoolRedpackDao.getById(id);
        if (Objects.isNull(ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("活动不存在");
        }
        EcologyPoolPrize ecologyPoolPrize = ecologyPoolPrizeService.getEcologyPoolPrizeByBindId(id, EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);
        if (Objects.isNull(ecologyPoolPrize)) {
            throw new GlobalRunTimeException("奖励配置不存在");
        }
        EcologyPool ecologyPool = ecologyPoolDao.getByTypeAndDaoId(EcologyPoolTypeEnum.BRAND, EcologyPoolLinkTypeEnum.POINTS, ecologyPoolRedPack.getDaoId());
        WebEcologyPoolRedPackDetailVO ecologyPoolRedPackDetailVO = new WebEcologyPoolRedPackDetailVO();
        String reward = EcologyPoolRedPackTypeEnum.POINTS.getDesc() + " * " + ecologyPoolPrize.getLinkCount();
        if (Objects.nonNull(ecologyPool) && StrUtil.isNotBlank(ecologyPool.getPoolName())) {
            reward = ecologyPool.getPoolName() + " * " + ecologyPoolPrize.getLinkCount();
        }
        ecologyPoolRedPackDetailVO.setReward(reward);
        ecologyPoolRedPackDetailVO.setPackNumber(ecologyPoolPrize.getLinkNumber());
        Long cacheResiduePackNumber = getCacheResiduePackNumber(ecologyPoolRedPack.getId());
        ecologyPoolRedPackDetailVO.setResiduePackNumber(cacheResiduePackNumber);
        ecologyPoolRedPackDetailVO.setStartTime(ecologyPoolRedPack.getStartTime());
        ecologyPoolRedPackDetailVO.setEndTime(ecologyPoolRedPack.getEndTime());
        ecologyPoolRedPackDetailVO.setActiveRule(ecologyPoolRedPack.getActiveRule());
        ecologyPoolRedPackDetailVO.setName(ecologyPoolRedPack.getName());
        ecologyPoolRedPackDetailVO.setParticipate(buildParticipate(ecologyPoolRedPack.getParticipate(), ecologyPoolRedPack.getId()));
        ecologyPoolRedPackDetailVO.setParticipateNumber(ecologyPoolRedPack.getParticipateNumber());
        ecologyPoolRedPackDetailVO.setAlreadyParticipateNumber(ecologyPoolRedPackReceiveService.countUserReceive(userId, ecologyPoolRedPack.getId()));
        //红包活动状态
        buildRedPackStatus(userId, ecologyPoolRedPackDetailVO, ecologyPoolRedPack, cacheResiduePackNumber);
        return ecologyPoolRedPackDetailVO;
    }


    @Override
    public IPage<WebEcologyPoolRedPackPageVO> redPackPage(Long daoId, Integer pageNo, Integer pageNum) {
        Page<WebEcologyPoolRedPackPageDO> webEcologyPoolRedPackPageDOPage = ecologyPoolRedpackDao.redPackPage(daoId, new Page<>(pageNo, pageNum));
        List<WebEcologyPoolRedPackPageDO> records = webEcologyPoolRedPackPageDOPage.getRecords();
        List<WebEcologyPoolRedPackPageVO> list = Lists.newArrayList();
        for (WebEcologyPoolRedPackPageDO record : records) {
            list.add(getWebEcologyPoolRedPackPageVO(record));
        }
        Page<WebEcologyPoolRedPackPageVO> pageVO = new Page<>();
        pageVO.setRecords(list);
        pageVO.setTotal(webEcologyPoolRedPackPageDOPage.getTotal());
        pageVO.setCurrent(webEcologyPoolRedPackPageDOPage.getCurrent());
        pageVO.setSize(webEcologyPoolRedPackPageDOPage.getSize());
        return pageVO;
    }

    @Override
    public void redPackOnSiteInspection() {
        List<EcologyPoolRedPack> ecologyPoolRedPacks = ecologyPoolRedpackDao.listByEndTimeAfter();
        for (EcologyPoolRedPack ecologyPoolRedPack : ecologyPoolRedPacks) {
            EcologyPoolPrize ecologyPoolPrize = ecologyPoolPrizeService.getEcologyPoolPrizeByBindId(ecologyPoolRedPack.getId(), EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);

            //查看领取数量
            int receiveCount = ecologyPoolRedPackReceiveService.countReceive(ecologyPoolRedPack.getId());
            String numberKey = String.format(RedisKeyEnum.RED_PACK_CACHE_NUMBER.getKey(), ecologyPoolRedPack.getId());
            String number = stringRedisTemplate.opsForValue().get(numberKey);
            //查看领取额度
            BigDecimal receiveSum = ecologyPoolRedPackReceiveService.sumReceive(ecologyPoolRedPack.getId());
            String countKey = String.format(RedisKeyEnum.RED_PACK_CACHE_COUNT.getKey(), ecologyPoolRedPack.getId());
            String count = stringRedisTemplate.opsForValue().get(countKey);


            if (StringUtils.isBlank(number)
                    || StringUtils.isBlank(count)
                    || ecologyPoolPrize.getLinkNumber() - receiveCount != Long.parseLong(number)
                    || ecologyPoolPrize.getLinkCount().subtract(receiveSum).compareTo(new BigDecimal(count)) != 0) {
                EcologyPoolRedPackCheck ecologyPoolRedPackCheck = new EcologyPoolRedPackCheck();
                ecologyPoolRedPackCheck.setEcologyPoolRedPackId(ecologyPoolRedPack.getId());
                ecologyPoolRedPackCheck.setLinkNumber(ecologyPoolPrize.getLinkNumber());
                ecologyPoolRedPackCheck.setLinkCount(ecologyPoolPrize.getLinkCount());
                ecologyPoolRedPackCheck.setRemainNumber(StringUtils.isBlank(number) ? 0L : Long.parseLong(number));
                ecologyPoolRedPackCheck.setRemainCount(StringUtils.isBlank(count) ? new BigDecimal(0) : new BigDecimal(count));
                ecologyPoolRedPackCheckDao.save(ecologyPoolRedPackCheck);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void redPackExpiredPointsRefund() {
        List<EcologyPoolRedPack> ecologyPoolRedPacks = ecologyPoolRedpackDao.listByRefundPoint();
        List<Long> ids = ecologyPoolRedPacks.stream().map(EcologyPoolRedPack::getDaoId).collect(Collectors.toList());
        Map<Long, UserCreatorInfo> creatorInfoMap = userCreatorInfoService.getDaoByIdsToMap(ids);
        log.info("redPackExpiredPointsRefund data:[{}]", ecologyPoolRedPacks);
        for (EcologyPoolRedPack ecologyPoolRedPack : ecologyPoolRedPacks) {
            doReducePool(ecologyPoolRedPack, creatorInfoMap);
        }
    }

    private void doReducePool(EcologyPoolRedPack ecologyPoolRedPack, Map<Long, UserCreatorInfo> creatorInfoMap) {
        EcologyPoolPrize ecologyPoolPrize = ecologyPoolPrizeService.getEcologyPoolPrizeByBindId(ecologyPoolRedPack.getId(), EcologyPoolPrizeBindTypeEnum.RED_PACK, EcologyPoolLinkTypeEnum.POINTS);
        if (Objects.nonNull(ecologyPoolPrize) && ecologyPoolPrize.getRemainCount().compareTo(BigDecimal.ZERO) > 0) {
            EcologyPoolIncreasePointsDTO build = EcologyPoolIncreasePointsDTO.builder()
                    .linkCount(ecologyPoolPrize.getRemainCount())
                    .linkType(EcologyPoolLinkTypeEnum.POINTS)
                    .daoId(ecologyPoolRedPack.getDaoId())
                    .poolType(EcologyPoolTypeEnum.BRAND)

                    .sourcePoolType(EcologyPoolTypeEnum.RED_PACK)
                    .sourceDaoId(ecologyPoolRedPack.getDaoId())
                    .sourceRelationId(ecologyPoolRedPack.getId())

                    .operationType(EcologyPoolOperationTypeEnum.RETURN_RED_PACK)
                    .transactionUuid(IdUtil.simpleUUID())
                    .activeOperatorName(EcologyPoolTypeEnum.RED_PACK.getDesc())
                    .passiveOperatorName(getDaoName(ecologyPoolRedPack.getDaoId(), creatorInfoMap))
                    .build();
            ecologyPoolService.increasePoints(build);

            //扣减剩余数量
            ecologyPoolPrizeService.reducePrize(ecologyPoolPrize);
        }
        ecologyPoolRedPack.setRefundStatus(Boolean.TRUE);
        //修改红包退还状态
        if (!ecologyPoolRedpackDao.updateRedPackById(ecologyPoolRedPack.getId(), ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("redPackExpiredPointsRefund 退还积分更新失败:" + ecologyPoolRedPack.getId());
        }
    }


    @Override
    public IPage<WebEcologyPoolParticipatePageVO> redPackParticipatePage(Long userId, Long daoId, Integer pageNo, Integer pageNum) {
        EcologyPool ecologyPool = ecologyPoolDao.getByTypeAndDaoId(EcologyPoolTypeEnum.BRAND, EcologyPoolLinkTypeEnum.POINTS, daoId);
        Page<EcologyPoolRedPackReceive> poolRedPackReceivePage = ecologyPoolRedPackReceiveService.redPackParticipatePage(userId, daoId, pageNo, pageNum);
        return poolRedPackReceivePage.convert(x -> {
            WebEcologyPoolParticipatePageVO webEcologyPoolParticipatePageVO = new WebEcologyPoolParticipatePageVO();
            EcologyPoolRedPack redPack = ecologyPoolRedpackDao.getById(x.getEcologyPoolRedPackId());
            webEcologyPoolParticipatePageVO.setName(redPack.getName());
            String reward = redPack.getType().getDesc() + " * " + x.getReceiveRewardNumber();
            if (Objects.equals(redPack.getType(), EcologyPoolRedPackTypeEnum.POINTS)
                    && Objects.nonNull(ecologyPool)
                    && StrUtil.isNotBlank(ecologyPool.getPoolName())) {
                reward = ecologyPool.getPoolName() + " * " + x.getReceiveRewardNumber();
            }
            webEcologyPoolParticipatePageVO.setReward(reward);
            webEcologyPoolParticipatePageVO.setCreateTime(x.getCreateTime());
            return webEcologyPoolParticipatePageVO;
        });
    }

    @Override
    public WebEcologyPoolRedPackStatusVO redPackStatus(Long daoId) {
        int statusCount = ecologyPoolRedpackDao.redPackStatus(daoId);
        WebEcologyPoolRedPackStatusVO webEcologyPoolRedPackStatusVO = new WebEcologyPoolRedPackStatusVO();
        webEcologyPoolRedPackStatusVO.setStatus(EcologyPoolRedPackStatusEnum.NOT_IN);
        webEcologyPoolRedPackStatusVO.setStatusName(EcologyPoolRedPackStatusEnum.NOT_IN.getDesc());
        if (statusCount > 0) {
            webEcologyPoolRedPackStatusVO.setStatus(EcologyPoolRedPackStatusEnum.IN);
            webEcologyPoolRedPackStatusVO.setStatusName(EcologyPoolRedPackStatusEnum.IN.getDesc());
        }
        return webEcologyPoolRedPackStatusVO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public WebEcologyPoolRedPackOpenVO openRedPack(Long userId, Long id) {
        EcologyPoolRedPack redPack = ecologyPoolRedpackDao.getById(id);

        checkOpenStatus(userId, id, redPack);

        String countKey = String.format(RedisKeyEnum.RED_PACK_CACHE_COUNT.getKey(), redPack.getId());
        String numberKey = String.format(RedisKeyEnum.RED_PACK_CACHE_NUMBER.getKey(), redPack.getId());

        //使用lua脚本进行红包的数量和额度扣减
        //返回的result是由三部分组成 由逗号隔开 比如 111,333,60 第一部分代表领取的数量 第二部分代表剩余数量 第三部分代表剩余个数
        //返回的result也有特殊情况 比如只返回一个-1 -2 代表红包额度为0和数量为0 就不会在拼接3个部分返回了
        String result = stringRedisTemplate.execute(redisScript, Arrays.asList(numberKey, countKey, String.valueOf(redPack.getMechanism().getCode()), String.valueOf(System.currentTimeMillis())));

        if (StringUtils.isBlank(result)) {
            throw new GlobalRunTimeException("红包打开失败,未初始化红包数值");
        }

        EcologyPoolRedPackReceive redPackReceive = getEcologyPoolRedPackReceive(userId, result, redPack);
        ecologyPoolRedPackReceiveService.saveRedPackReceive(redPackReceive);

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                //发送mq消息
                OpenRedPackMessageDTO openRedPackMessageDTO = new OpenRedPackMessageDTO();
                openRedPackMessageDTO.setReceiveId(redPackReceive.getId());
                openRedPackMessageDTO.setRedPackId(redPack.getId());
                openRedPackMessageDTO.setUserId(userId);
                messageQueueHelper.sendMessage(MessageTopic.ECOLOGY_POOL_OPEN_READ_PACK, openRedPackMessageDTO, EcologyPoolRedPackEnum.ECOLOGY_POOL_OPEN_RED_PACK.name(), EcologyPoolRedPackEnum.OPEN_READ_PACK_RECEIVE.getDesc() + redPackReceive.getId());
            }
        });

        EcologyPool ecologyPool = ecologyPoolDao.getByTypeAndDaoId(EcologyPoolTypeEnum.BRAND, EcologyPoolLinkTypeEnum.POINTS, redPack.getDaoId());
        WebEcologyPoolRedPackOpenVO webEcologyPoolRedPackOpenVO = new WebEcologyPoolRedPackOpenVO();
        String reward = redPack.getType().getDesc() + "*" + redPackReceive.getReceiveRewardNumber();
        if (Objects.equals(redPack.getType(), EcologyPoolRedPackTypeEnum.POINTS)
                && Objects.nonNull(ecologyPool)
                && StrUtil.isNotBlank(ecologyPool.getPoolName())) {
            reward = ecologyPool.getPoolName() + "*" + redPackReceive.getReceiveRewardNumber();
        }
        webEcologyPoolRedPackOpenVO.setReward(reward);
        return webEcologyPoolRedPackOpenVO;
    }


    private String getDaoName(Long daoId, Map<Long, UserCreatorInfo> creatorInfoMap) {
        String daoName = "无";
        UserCreatorInfo userCreatorInfo = creatorInfoMap.get(daoId);
        if (Objects.nonNull(userCreatorInfo)) {
            daoName = userCreatorInfo.getDaoName();
        }
        return daoName;
    }


    private void updateRedPackAuditStatus(Long id, EcologyPoolRedPackAuditStatusEnum ecologyPoolRedPackAuditStatusEnum, String rejectReason, EcologyPoolRedPack ecologyPoolRedPack) {
        EcologyPoolRedPack poolRedPack = new EcologyPoolRedPack();
        if (ecologyPoolRedPackAuditStatusEnum == EcologyPoolRedPackAuditStatusEnum.APPROVED) {
            poolRedPack.setStatus(Boolean.TRUE);
        }
        if (StringUtils.isNotBlank(rejectReason)) {
            poolRedPack.setRejectReason(rejectReason);
        }
        poolRedPack.setAuditStatus(ecologyPoolRedPackAuditStatusEnum);
        poolRedPack.setVersion(ecologyPoolRedPack.getVersion());
        if (!ecologyPoolRedpackDao.updateRedPackById(id, poolRedPack)) {
            throw new GlobalRunTimeException("红包活动审核失败!");
        }
    }

    private static WebEcologyPoolRedPackPageVO getWebEcologyPoolRedPackPageVO(WebEcologyPoolRedPackPageDO record) {
        WebEcologyPoolRedPackPageVO webEcologyPoolRedPackPageVO = new WebEcologyPoolRedPackPageVO();
        webEcologyPoolRedPackPageVO.setEndTime(record.getEndTime());
        webEcologyPoolRedPackPageVO.setId(record.getId());
        webEcologyPoolRedPackPageVO.setStartTime(record.getStartTime());
        webEcologyPoolRedPackPageVO.setName(record.getName());
        //这里的1  2  3 代表sql中返回的排序后的状态
        if (record.getStatus() == 1) {
            webEcologyPoolRedPackPageVO.setStatus(EcologyPoolRedPackPageStatusEnum.IN_PROGRESS);
            webEcologyPoolRedPackPageVO.setStatusName(EcologyPoolRedPackPageStatusEnum.IN_PROGRESS.getDesc());
        } else if (record.getStatus() == 2) {
            webEcologyPoolRedPackPageVO.setStatus(EcologyPoolRedPackPageStatusEnum.BEGIN_IN_MINUTE);
            webEcologyPoolRedPackPageVO.setStatusName(EcologyPoolRedPackPageStatusEnum.BEGIN_IN_MINUTE.getDesc());
        } else if (record.getStatus() == 3) {
            webEcologyPoolRedPackPageVO.setStatus(EcologyPoolRedPackPageStatusEnum.ENDED);
            webEcologyPoolRedPackPageVO.setStatusName(EcologyPoolRedPackPageStatusEnum.ENDED.getDesc());
        }
        return webEcologyPoolRedPackPageVO;
    }

    private Long getCacheResiduePackNumber(Long id) {
        String numberKey = String.format(RedisKeyEnum.RED_PACK_CACHE_NUMBER.getKey(), id);
        String residueNum = stringRedisTemplate.opsForValue().get(numberKey);
        if (StringUtils.isNotBlank(residueNum)) {
            return Long.parseLong(residueNum);
        }
        return 0L;
    }


    private void checkOpenStatus(Long userId, Long id, EcologyPoolRedPack redPack) {
        if (Objects.isNull(redPack)) {
            throw new GlobalRunTimeException("红包不存在");
        }

        if (redPack.getAuditStatus() != EcologyPoolRedPackAuditStatusEnum.APPROVED) {
            throw new GlobalRunTimeException("红包状态异常,无法抢红包");
        }

        LocalDateTime startTime = LocalDateTimeUtil.dateToLocalDateTime(redPack.getStartTime());
        LocalDateTime endTime = LocalDateTimeUtil.dateToLocalDateTime(redPack.getEndTime());
        LocalDateTime now = LocalDateTime.now();
        if (!((now.isAfter(startTime) || now.isEqual(startTime)) && now.isBefore(endTime) || now.isEqual(endTime))) {
            throw new GlobalRunTimeException("活动时间不再开启范围内,无法抢红包");
        }

        int count = ecologyPoolRedPackReceiveService.countUserReceive(userId, id);
        if (count >= redPack.getParticipateNumber()) {
            throw new GlobalRunTimeException("无参与次数");
        }
        if (!redPack.getParticipate()) {
            return;
        }
        List<EcologyPoolRelationConditionBind> list = ecologyPoolRelationConditionBindService.listByRedPackBind(redPack.getId());
        if (CollectionUtils.isEmpty(list)) {
            throw new GlobalRunTimeException("参与条件配置异常,请检查!");
        }
        List<Long> nftIds = list.stream().map(EcologyPoolRelationConditionBind::getRelationId).collect(Collectors.toList());
        Integer collectionSize = userCollectionService.getHoldCountByCollectionIdsAndUserId(userId, Lists.newArrayList(nftIds));
        if (collectionSize <= 0) {
            throw new GlobalRunTimeException("不满足参与条件");
        }
    }

    private static EcologyPoolRedPackReceive getEcologyPoolRedPackReceive(Long userId, String result, EcologyPoolRedPack redPack) {
        String[] split = result.split(",");
        long currentCount = Long.parseLong(split[0]);//领取额度
        if (currentCount == -1) {
            throw new GlobalRunTimeException(ErrorCode.OPEN_RED_PACK_ERROR);
        }
        if (currentCount == -2) {
            throw new GlobalRunTimeException(ErrorCode.OPEN_RED_PACK_RESIDUE);
        }

        long residueCount = Long.parseLong(split[1]);//剩余额度
        long residueNumber = Long.parseLong(split[2]);//剩余数量

        EcologyPoolRedPackReceive redPackReceive = new EcologyPoolRedPackReceive();
        redPackReceive.setDaoId(redPack.getDaoId());
        redPackReceive.setEcologyPoolRedPackId(redPack.getId());
        redPackReceive.setUserId(userId);
        redPackReceive.setReceivePackNumber(1L);
        redPackReceive.setResiduePackNumber(residueNumber);
        redPackReceive.setReceiveRewardNumber(new BigDecimal(currentCount).divide(new BigDecimal(100), 2, RoundingMode.DOWN));
        redPackReceive.setResidueRewardNumber(new BigDecimal(residueCount).divide(new BigDecimal(100), 2, RoundingMode.DOWN));
        return redPackReceive;
    }

    private void buildRedPackStatus(Long userId, WebEcologyPoolRedPackDetailVO ecologyPoolRedPackDetailVO, EcologyPoolRedPack ecologyPoolRedPack, Long cacheResiduePackNumber) {
        ecologyPoolRedPackDetailVO.setStatus(EcologyPoolRedPackOpenEnum.NOT_STARTED);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startTime = LocalDateTimeUtil.dateToLocalDateTime(ecologyPoolRedPack.getStartTime());
        LocalDateTime endTime = LocalDateTimeUtil.dateToLocalDateTime(ecologyPoolRedPack.getEndTime());
        boolean isInRange = now.isAfter(startTime) && now.isBefore(endTime);
        if (isInRange) {
            ecologyPoolRedPackDetailVO.setStatus(EcologyPoolRedPackOpenEnum.IN_PROGRESS);
        }
        if (cacheResiduePackNumber <= 0) {
            ecologyPoolRedPackDetailVO.setStatus(EcologyPoolRedPackOpenEnum.FINISHED_SNATCHING);
        }
        if (endTime.isBefore(now)) {
            ecologyPoolRedPackDetailVO.setStatus(EcologyPoolRedPackOpenEnum.ENDED);
        }
        List<EcologyPoolRelationConditionBind> list = ecologyPoolRelationConditionBindService.listByRedPackBind(ecologyPoolRedPack.getId());
        if (CollectionUtils.isNotEmpty(list)) {
            List<Long> nftIds = list.stream().map(EcologyPoolRelationConditionBind::getRelationId).collect(Collectors.toList());
            Integer collectionSize = userCollectionService.getHoldCountByCollectionIdsAndUserId(userId, Lists.newArrayList(nftIds));
            if (collectionSize <= 0) {
                ecologyPoolRedPackDetailVO.setStatus(EcologyPoolRedPackOpenEnum.CONDITION_NOT);
            }
        }
    }

    private static void checkStatus(EcologyPoolRedPack ecologyPoolRedPack) {
        if (Objects.isNull(ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("红包活动不存在");
        }
        if (ecologyPoolRedPack.getAuditStatus() != EcologyPoolRedPackAuditStatusEnum.IN_REVIEW) {
            throw new GlobalRunTimeException("红包状态不在待审核状态!");
        }
    }

    private void doReject(EcologyPoolRedPackAuditStatusEnum ecologyPoolRedPackAuditStatusEnum, EcologyPoolRedPack ecologyPoolRedPack, EcologyPoolPrize ecologyPoolPrize) {
        if (EcologyPoolRedPackAuditStatusEnum.REVIEW_REJECTED != ecologyPoolRedPackAuditStatusEnum) {
            return;
        }

        Map<Long, UserCreatorInfo> creatorInfoMap = userCreatorInfoService.getDaoByIdsToMap(Lists.newArrayList(ecologyPoolRedPack.getDaoId()));
        doReducePool(ecologyPoolRedPack, creatorInfoMap);
    }


    private void doApproved(EcologyPoolRedPackAuditStatusEnum ecologyPoolRedPackAuditStatusEnum, EcologyPoolRedPack ecologyPoolRedPack, EcologyPoolPrize ecologyPoolPrize) {
        if (EcologyPoolRedPackAuditStatusEnum.APPROVED != ecologyPoolRedPackAuditStatusEnum) {
            return;
        }
        String countKey = String.format(RedisKeyEnum.RED_PACK_CACHE_COUNT.getKey(), ecologyPoolRedPack.getId());
        String numberKey = String.format(RedisKeyEnum.RED_PACK_CACHE_NUMBER.getKey(), ecologyPoolRedPack.getId());

        BigDecimal t1 = new BigDecimal("0.01").multiply(new BigDecimal(ecologyPoolPrize.getLinkNumber()));
        BigDecimal t2 = ecologyPoolPrize.getLinkCount().subtract(t1);
        BigDecimal t3 = t2.multiply(new BigDecimal(100)).setScale(0, RoundingMode.DOWN);

        //设置红包额度和个数
        stringRedisTemplate.opsForValue().set(countKey, t3.toPlainString());
        stringRedisTemplate.opsForValue().set(numberKey, ecologyPoolPrize.getLinkNumber().toString());
        //设置过期时间
        LocalDateTime startTime = LocalDateTimeUtil.dateToLocalDateTime(ecologyPoolRedPack.getStartTime());
        LocalDateTime endTime = LocalDateTimeUtil.dateToLocalDateTime(ecologyPoolRedPack.getEndTime());
        long between = ChronoUnit.DAYS.between(startTime, endTime);
        stringRedisTemplate.expire(countKey, between + 3, TimeUnit.DAYS);
        stringRedisTemplate.expire(numberKey, between + 3, TimeUnit.DAYS);
    }

    private void saveRedPackNumber(BigDecimal rewardNumber, Long packNumber, EcologyPoolRedPack ecologyPoolRedPack) {
        EcologyPoolPrize ecologyPoolPrize = new EcologyPoolPrize();
        ecologyPoolPrize.setBindId(ecologyPoolRedPack.getId());
        ecologyPoolPrize.setBindType(EcologyPoolPrizeBindTypeEnum.RED_PACK);
        ecologyPoolPrize.setLinkType(EcologyPoolLinkTypeEnum.POINTS);
        ecologyPoolPrize.setLinkCount(rewardNumber);
        ecologyPoolPrize.setRemainCount(rewardNumber);
        ecologyPoolPrize.setLinkNumber(packNumber);
        ecologyPoolPrize.setRemainNumber(packNumber);
        ecologyPoolPrize.setVersion(1);
        ecologyPoolPrizeService.savePrize(ecologyPoolPrize);
    }

    private void saveNftBind(Boolean participateType, List<Long> participateNft, EcologyPoolRedPack ecologyPoolRedPack) {
        if (!participateType) {
            return;
        }
        List<EcologyPoolRelationConditionBind> list = Lists.newArrayList();
        for (Long nftId : participateNft) {
            EcologyPoolRelationConditionBind ecologyPoolRelationConditionBind = new EcologyPoolRelationConditionBind();
            ecologyPoolRelationConditionBind.setBindId(ecologyPoolRedPack.getId());
            ecologyPoolRelationConditionBind.setRelationId(nftId);
            ecologyPoolRelationConditionBind.setBindType(EcologyPoolRelationConditionBindTypeEnum.RED_PACK);
            ecologyPoolRelationConditionBind.setRelationType(EcologyPoolRelationTypeEnum.COLLECTION);
            list.add(ecologyPoolRelationConditionBind);
        }
        ecologyPoolRelationConditionBindService.saveRedPackNftBind(list);
    }

    private EcologyPoolRedPack saveEcologyPoolRedPack(Long daoId, String name, EcologyPoolRedPackMechanismEnum mechanism, EcologyPoolRedPackTypeEnum type, Date startTime, Date endTime, String activeRule, Boolean participateType, Integer participateNumber) {
        EcologyPoolRedPack ecologyPoolRedPack = new EcologyPoolRedPack();
        ecologyPoolRedPack.setDaoId(daoId);
        ecologyPoolRedPack.setName(name);
        ecologyPoolRedPack.setMechanism(mechanism);
        ecologyPoolRedPack.setType(type);
        ecologyPoolRedPack.setStartTime(startTime);
        ecologyPoolRedPack.setEndTime(endTime);
        ecologyPoolRedPack.setActiveRule(activeRule);
        ecologyPoolRedPack.setParticipate(participateType);
        ecologyPoolRedPack.setParticipateNumber(participateNumber);
        ecologyPoolRedPack.setVersion(1);
        ecologyPoolRedPack.setAuditStatus(EcologyPoolRedPackAuditStatusEnum.IN_REVIEW);

        if (!ecologyPoolRedpackDao.save(ecologyPoolRedPack)) {
            throw new GlobalRunTimeException("红包活动创建失败");
        }
        return ecologyPoolRedPack;
    }

    private void checkBrandPoint(Long daoId, EcologyPoolRedPackTypeEnum type, BigDecimal rewardNumber) {
        EcologyPool daoPool = ecologyPoolService.getDaoPool(daoId);
        if (Objects.isNull(daoPool)) {
            throw new GlobalRunTimeException("品牌积分不存在");
        }
        if (daoPool.getLinkCount().compareTo(rewardNumber) < 0) {
            throw new GlobalRunTimeException("创建失败，当前" + type.getDesc() + "不足");
        }
    }

    private EcologyPoolRedPackPageVO getDaoRedPackPageVO(EcologyPoolRedPack x, Map<Long, UserCreatorInfo> daoByIdsToMap, Map<Long, EcologyPoolPrize> longEcologyPoolPrizeMap) {
        EcologyPoolRedPackPageVO vo = new EcologyPoolRedPackPageVO();
        vo.setId(x.getId());
        vo.setName(x.getName());
        UserCreatorInfo userCreatorInfo = daoByIdsToMap.get(x.getDaoId());
        if (Objects.nonNull(userCreatorInfo)) {
            vo.setDaoName(userCreatorInfo.getDaoName());
        }
        EcologyPoolPrize ecologyPoolPrize = longEcologyPoolPrizeMap.get(x.getId());
        if (Objects.nonNull(ecologyPoolPrize)) {
            vo.setRewardContent(EcologyPoolRedPackTypeEnum.POINTS.getDesc() + " * " + ecologyPoolPrize.getLinkCount());
            vo.setPackNumber(ecologyPoolPrize.getLinkNumber());
        }
        String participate = buildParticipate(x.getParticipate(), x.getId());
        vo.setParticipate(StringUtils.isNotBlank(participate) ? participate : "无");
        vo.setStartTime(x.getStartTime());
        vo.setEndTime(x.getEndTime());
        vo.setAuditStatus(x.getAuditStatus());
        vo.setAuditStatusName(x.getAuditStatus().getDesc());
        vo.setStatus(x.getStatus());
        vo.setStatusName(x.getStatus() ? "开启" : "关闭");
        vo.setCreateTime(x.getCreateTime());
        vo.setRejectReason(x.getRejectReason());
        return vo;
    }

    //获取参与条件的关联藏品
    private String buildParticipate(Boolean participate, Long id) {
        if (!participate) {
            return null;
        }
        List<EcologyPoolRelationConditionBind> list = ecologyPoolRelationConditionBindService.listByRedPackBind(id);
        List<String> nameList = Lists.newArrayList();
        List<Long> nftIds = list.stream().map(EcologyPoolRelationConditionBind::getRelationId).collect(Collectors.toList());
        List<DigitalCollection> nftList = digitalCollectionService.getCollectionListById(nftIds);
        for (DigitalCollection digitalCollection : nftList) {
            nameList.add(digitalCollection.getFirstTitle());
        }
        return StringUtils.join(nameList, ",");
    }

}